/*
 * This source code is part of TWaver 4.0
 *
 * Serva Software PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * Copyright 2002 - 2011 Serva Software. All rights reserved.
 */

package demo.editor.drawing;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
import java.util.List;

import twaver.BaseElement;
import twaver.DataBoxSelectionModel;
import twaver.Element;
import twaver.ResizableNode;
import twaver.ShapeNode;
import twaver.network.CanvasCushion;
import twaver.network.InteractionEvent;
import twaver.network.InteractionListener;
import twaver.network.TNetwork;

public class GridSnapper{
	
    private TNetwork network = null;
    private int gridSize = 10;
    private Color color = new Color(238, 238, 238);
    Stroke stroke = new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 5.0f, new float[] {5}, 0.0f);
    private boolean showGrid = false;
    
    public GridSnapper(final TNetwork network) {
        this.network = network;
        
        this.network.addCanvasCushion(new CanvasCushion(){
			public void paint(Graphics2D g2d) {
		        Rectangle rect = g2d.getClipBounds();
		        if(!showGrid || rect == null){
		        	return;
		        }
		        
	            g2d.setColor(color);
	            g2d.setStroke(stroke);

	            int X1 = rect.x;
	            int Y1 = rect.y;
	            int X2 = rect.x + rect.width;
	            int Y2 = rect.y + rect.height;
	            
	            X1 = X1 - X1 % gridSize;
	            Y1 = Y1 - Y1 % gridSize;
	            X2 = X2 + (gridSize - X2 % gridSize);
	            Y2 = Y2 + (gridSize - Y2 % gridSize);

	            for (int x = X1; x <= X2; x += gridSize) {
	                g2d.drawLine(x, Y1, x, Y2);
	            }

	            for (int y = Y1; y <= Y2; y += gridSize) {
	                g2d.drawLine(X1, y, X2, y);
	            }
			}
        });
        
        this.network.addInteractionListener(new InteractionListener(){
			public void interactionPerformed(InteractionEvent event) {
				int type = event.getType();
				if(type == InteractionEvent.ELEMENT_LEAP_MOVED 
					|| type == InteractionEvent.ELEMENT_SMOOTH_MOVED	
					|| type == InteractionEvent.ELEMENT_RESIZED){
					DataBoxSelectionModel selectionModel = network.getDataBox().getSelectionModel();
					if(selectionModel.size() == 1){
						snapElement(selectionModel.lastElement());
					}
				}
				else if(type == InteractionEvent.ELEMENT_POINT_MOVED){
					Integer index = (Integer)event.getUserObject();
					snapElementPoint((Element)event.getSource(), index.intValue());
				}
			}
        });
    }

    public void snapElementPoint(Element element, int pointIndex){
        // fix points
        if (element instanceof ShapeNode) {
        	ShapeNode shapeNode = (ShapeNode)element;
        	List list = shapeNode.getPoints();

    		Point2D point = (Point2D)list.get(pointIndex);
            double fixedX = point.getX();
            double fixedY = point.getY();
            double xOffset = point.getX() % gridSize;
            double yOffset = point.getY() % gridSize;
            if (xOffset != 0) {
                if (xOffset > gridSize / 2) {
                    fixedX = point.getX() - xOffset + gridSize;
                } else {
                    fixedX = point.getX() - xOffset;
                }
            }
            if (yOffset != 0) {
                if (yOffset > gridSize / 2) {
                    fixedY = point.getY() - yOffset + gridSize;
                } else {
                    fixedY = point.getY() - yOffset;
                }
            }
            shapeNode.setPoint(pointIndex, new Double(fixedX, fixedY));
    	}
    }

    public void snapElement(Element element) {
    	
        if (element instanceof BaseElement) {
            //1. fix location.
            int fixedX = element.getLocation().x;
            int fixedY = element.getLocation().y;
            int xOffset = element.getLocation().x % gridSize;
            int yOffset = element.getLocation().y % gridSize;
            if (xOffset != 0) {
                if (xOffset > gridSize / 2) {
                    fixedX = element.getLocation().x - xOffset + gridSize;
                } else {
                    fixedX = element.getLocation().x - xOffset;
                }
            }
            if (yOffset != 0) {
                if (yOffset > gridSize / 2) {
                    fixedY = element.getLocation().y - yOffset + gridSize;
                } else {
                    fixedY = element.getLocation().y - yOffset;
                }
            }
            element.setLocation(fixedX, fixedY);
            
            //2. fix size.
            if (element instanceof ResizableNode) {
                int fixedWidth = element.getWidth();
                int fixedHeight = element.getHeight();
                int widthOffset = element.getWidth() % gridSize;
                int heightOffset = element.getHeight() % gridSize;
                if (widthOffset != 0) {
                    if (widthOffset > gridSize / 2) {
                        fixedWidth = element.getWidth() - widthOffset + gridSize;
                    } else {
                        fixedWidth = element.getWidth() - widthOffset;
                    }
                }
                if (heightOffset != 0) {
                    if (heightOffset > gridSize / 2) {
                        fixedHeight = element.getHeight() - heightOffset + gridSize;
                    } else {
                        fixedHeight = element.getHeight() - heightOffset;
                    }
                }
                fixedWidth = fixedWidth == 0? gridSize : fixedWidth;
                fixedHeight = fixedHeight == 0? gridSize : fixedHeight;
                ( (ResizableNode) element).setSize(fixedWidth, fixedHeight);
            }
        }
        
        // fix points
        if (element instanceof ShapeNode) {
        	ShapeNode shapeNode = (ShapeNode)element;
        	List list = shapeNode.getPoints();
        	for(int i=0; i<list.size(); i++){
        		snapElementPoint(shapeNode, i);
        	}
        	shapeNode.firePointsChange();
        }
    }

	public boolean isShowGrid() {
		return showGrid;
	}

	public void setShowGrid(boolean showGrid) {
		this.showGrid = showGrid;
		this.network.getCanvas().repaint();
	}

}